
#include "test_case.hpp"

/*
g++ test_case.cpp -static -I../insdir/include -L../insdir/lib -lboost_system -lpthread -lboost_coroutine -lboost_context
*/

int test_case0_p11_boost_version(void) {
    using namespace std;

    cout << BOOST_VERSION << endl;
    cout << BOOST_LIB_VERSION << endl;
    cout << BOOST_PLATFORM << endl;
    cout << BOOST_COMPILER << endl;
    //cout << BOOST_STDLIB << endl;
    return 0;
}

void test_case1_p550_sync_steady_timer1(void) {

    io_service io;
    //boost::posix_time::millisec(500)
    //millisec ms(1000);
    using namespace std::literals::chrono_literals;
    std::chrono::milliseconds d1 = 5000ms;
    steady_timer t(io, d1);
  
    t.wait();
    std::cout << "hello asio" << std::endl;

}

void test_case2_p550_sync_stready_timer2(void) {

    std::cout << "hellworold" << std::endl;
    io_service io;  //必须的io_service对象
 
    steady_timer t1(io);
    using namespace std::literals::chrono_literals;
    t1.expires_from_now(std::chrono::milliseconds(5000ms));

    //working
    //steady_timer t1(io, std::chrono::milliseconds(5000));

    //not working, not sure the reason
    //std::cout << t1.expires_at() << std::endl; //查看终止的时间点，单位是纳秒
    //std::cout << t1.expires_from_now().total_seconds() << std::endl;    //查看终止的时间长度，单位是纳秒
    //std::cout<<"expire after "<< t1.expires_from_now().total_seconds()<<"s\n";

    t1.wait(); //调用wait()同步等待
    std::cout << "Hello World!\n";
    getchar();

}

void test_case3_p551_async_timer(void) {
    io_service io;
    //
    using namespace std::literals::chrono_literals;
    steady_timer t(io, std::chrono::milliseconds(5000ms));
    t.async_wait( 
        [](const boost::system::error_code& ec) {
            std::cout << "hello asio" << std::endl;
        }
    );
    io.run();
    std::cout << "exit test_case3_p551_async_timer" << std::endl;
}


void test_case4_p556_ip_address(void) {
    ip::address addr;
    addr = addr.from_string("127.0.0.1");
    assert(addr.is_v4());
    std::cout << "ip address is " << addr.to_string() << std::endl;

    addr = addr.from_string("ab::12:34:56");
    assert(addr.is_v6());
    std::cout << "ip address is " << addr.to_string() << std::endl;
}


void test_case4_p557_endpoint(void) {
    ip::address addr;
    addr = addr.from_string("127.0.0.1");
    ip::tcp::endpoint ep(addr, 6688);
    std::cout << "ip address is " << addr.to_string() << " port " << ep.port() << std::endl;

}

/* 
    tcp server listen on port 6688
*/
void test_case5__p561_sync_tcp_server(void) {
     
    try {
        typedef ip::tcp::acceptor acceptor_type;
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        std::cout << "server start." << std::endl;
        io_service io;
        acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(), 6688));
        std::cout << acceptor.local_endpoint().address() << std::endl;
        for (;;) {
            socket_type sock(io);
            acceptor.accept(sock);
            std::cout << "client:";
            std::cout << sock.remote_endpoint().address() << " port " << sock.remote_endpoint().port() << std::endl;
            sock.send(buffer("hello asio"));
            sock.close();
        }
    } catch(std::exception& ex) {
        std::cout << ex.what() << std::endl;
    }
}

void test_case6_p561_sync_tcp_client(void) {
    
    try {
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        typedef ip::address address_type;
        std::cout << "client start." << std::endl;
        io_service io;
        socket_type sock(io);
        endpoint_type ep(address_type::from_string("127.0.0.1"), 6688);
        sock.connect(ep);
        
        {
            using namespace std::literals::chrono_literals;
            std::chrono::milliseconds d1 = 5000ms;
            steady_timer t(io, d1);
            t.wait();
        }

        std::cout << "sock.available " << sock.available() << std::endl;
        std::vector<char> str(sock.available() + 1, 0);
        sock.receive(buffer(str));
        std::cout << "receive from " << sock.remote_endpoint().address() << std::endl;
        std::cout << &str[0] << std::endl;
    } catch(std::exception& ex) {
        std::cout << ex.what() << std::endl;
    }
}


void test_case6_p561_sync_tcp_client_small_packet(void) {
    
    try {
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        typedef ip::address address_type;
        std::cout << "client start." << std::endl;
        io_service io;
        socket_type sock(io);
        endpoint_type ep(address_type::from_string("127.0.0.1"), 6688);
        sock.connect(ep);
        std::cout << "receive from " << sock.remote_endpoint().address() << std::endl;
        {
            std::vector<char> str(5, 0);
            boost::system::error_code ec;
            for (;;) {
                sock.read_some(buffer(str), ec);
                if (ec) {
                    std::cout << "ec " << ec << " message " << ec.message() << std::endl;
                    break;
                }
                std::cout << &str[0] << std::endl;
            }
        }
    } catch(std::exception& ex) {
        std::cout << ex.what() << std::endl;
    }
}



void test_case7_p564_async_tcp_server() {

    class server {

        typedef server this_type;
        typedef ip::tcp::acceptor acceptor_type;
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket   socket_type;
        typedef std::shared_ptr<socket_type> sock_ptr;
        //typedef ip::tcp::socket * sock_ptr;

    private:
        io_service          m_io;
        acceptor_type       m_acceptor;

    public:
        server(): m_acceptor(m_io, endpoint_type(ip::tcp::v4(), 6699)) {
            accept();
        }
        
        void run() {
            m_io.run(); 
        }
        
        void accept() {
            sock_ptr sock(new socket_type(m_io));
            m_acceptor.async_accept(*sock, std::bind(&this_type::accept_handler, this, boost::asio::placeholders::error, sock));
        }
        
        void accept_handler(const boost::system::error_code& ec, sock_ptr sock) {
            if (ec) {
                return ;
            }

            std::cout << "client: ";
            std::cout << sock->remote_endpoint().address() << " port " << sock->remote_endpoint().port() << std::endl;
            sock->async_write_some(buffer("hello asio"), 
                std::bind(&this_type::write_handler, this, 
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

            accept();
        }

        // void write_handler(const boost::system::error_code&) {
        //     std::cout << "send msg " << std::endl;
        // }

        void write_handler(const boost::system::error_code&, std::size_t n) {
            std::cout << "send msg " << n << std::endl;
        }
    };

    try {
        std::cout << "server start." << std::endl;
        server srv;
        srv.run();
    } catch(std::exception& e) {
        std::cout << e.what() << std::endl;
    }
}

void test_case8_p565_async_tcp_client(void) {

    class client {
        typedef client              this_type;
        typedef ip::tcp::endpoint   endpoint_type;
        typedef ip::address         address_type;
        typedef ip::tcp::socket     socket_type;
        typedef std::shared_ptr<socket_type> sock_ptr;
        typedef std::vector<char>    buffer_type;

    private:
        io_service      m_io;
        buffer_type     m_buf;
        endpoint_type   m_ep;
    public:
        
        client(): m_buf(100, 0), 
                m_ep(address_type::from_string("127.0.0.1"), 6699) {
            start();
        }

        void run() {
            m_io.run();
        }

        void start() {
            sock_ptr sock(new socket_type(m_io));
            sock->async_connect(m_ep, 
                    std::bind(&this_type::conn_handler, this , 
                        boost::asio::placeholders::error, sock));
        }

        void conn_handler(const error_code& ec, sock_ptr sock) {
            if (ec) {
                return ;
            }
            std::cout << "receive from " << sock->remote_endpoint().address() << std::endl;
            sock->async_read_some(buffer(m_buf), 
                        std::bind(&client::read_handler, this , boost::asio::placeholders::error, sock));
        }

        void read_handler(const error_code& ec, sock_ptr sock) {
            if (ec) {
                return ;
            }
            std::cout << &m_buf[0] << std::endl;
        }
    };

    try {
        std::cout << "client start." << std::endl;
        client cl;
        cl.run();
    } catch(std::exception& e) {
        std::cout << e.what() << std::endl;
    }

}

//TODO
void test_case8_p567_async_tcp_server_lambda(void) {

}

//TODO
void test_case8_p567_async_tcp_client_lambda(void) {

}

void test_case9_p572_async_coroutine_tcp_server(void) {
    
    typedef ip::tcp::acceptor   acceptor_type;
    typedef ip::tcp::endpoint   endpoint_type;
    typedef ip::tcp::socket     socket_type;
    
    io_service io;

    spawn(io, [&](yield_context yield) {
        acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(), 6699));
        for(;;) {
            socket_type sock(io);
            error_code ec;
            acceptor.async_accept(sock, yield[ec]);
            
            if (ec) {
                return ;
            }
            auto len = sock.async_write_some(
                buffer("hello coroutine"), yield);
            std::cout << "send " << len << " bytes " << std::endl;
        }

    });
    
    io.run();
}


    void coroutine_function(boost::coroutines2::coroutine<void>::pull_type & coro_back)
    {
        std::cout << "a ";
        coro_back(); // 锚点，返回
        std::cout << "b ";
        coro_back(); //锚点 返回
        std::cout << "c ";
    }

void test_case10_coroutines2() {

    boost::coroutines2::coroutine<void>::push_type coroutine_object(coroutine_function);     // 创建协程
    std::cout << "1 ";
    coroutine_object(); // 运行协程
    std::cout << "2 ";
    coroutine_object(); // 返回锚点，继续运行协程
    std::cout << "3 ";
    coroutine_object(); // 返回锚点，继续运行协程
}

void test_case11_p573_async_connect_timeout() {
    
    io_service io;
    ip::tcp::socket sock(io);
    ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 6688);
    sock.async_connect(ep, [](const error_code&) {});

    steady_timer t(io, std::chrono::milliseconds(5000ms));
    t.async_wait( 
        [&](const error_code&) {
            std::cout << "time expired "  << std::endl;
            sock.close();
        }
    );
    io.run();
}


void test_case12_p575_udp_server() {
    
    std::cout << "udp server start." << std::endl;
    io_service io;

    ip::udp::socket sock(io, ip::udp::endpoint(ip::udp::v4(), 6699));

    for (;;) {
        char buf[1];
        ip::udp::endpoint ep;
        error_code ec;
        sock.receive_from(buffer(buf), ep, 0, ec);

        if (ec && ec != error::message_size) {
            throw system_error(ec);
        }

        std::cout << "send to " << ep.address() << std::endl;
        sock.send_to(buffer("hello asio udp"), ep);
    }
}

void test_case12_p575_udp_client() {
    
    std::cout << "client start." << std::endl;
    io_service io;
    ip::udp::endpoint send_ep(ip::address::from_string("127.0.0.1"), 6699);

    ip::udp::socket sock(io);
    sock.open(ip::udp::v4());
    char buf[1];
    sock.send_to(buffer(buf), send_ep);

    std::vector<char> v(100, 0);
    ip::udp::endpoint recv_ep;
    sock.receive_from(buffer(v), recv_ep);
    std::cout << "recv from " << recv_ep.address() << std::endl;
    std::cout << &v[0] << std::endl; 
} 


void foo(boost::coroutines2::coroutine<void>::push_type & sink){
    std::cout << "a=";
    sink();
    std::cout << "b=";
    sink();
    std::cout << "c=";
}
/*
原文链接：https://blog.csdn.net/lisemi/article/details/98656309
例子1：交替运行
*/
void test_case20_coroutine2() {

    std::cout << "start" << std::endl;
    boost::coroutines2::coroutine<void>::pull_type source(foo);
    std::cout << "1 ";
    source();
    std::cout << "2 ";
    source();
    std::cout << "3 ";
}

/*
例子2：协程环境传递数据给主环境
*/
void test_case20_coroutine3() {
    
    typedef boost::coroutines2::coroutine<int>   coro_t2;

    coro_t2::pull_type source(
     [&](coro_t2::push_type& sink){
            std::cout<< " coroutine 1" << std::endl;   
            sink(1);    // push {1} back to main-context
            std::cout<< " coroutine 2" << std::endl;
            sink(2);
            std::cout<< " coroutine 3" << std::endl;
            sink(3);
        });
    while(source)
    {
        int ret=source.get();       // pushed sink() data
        std::cout<< "move to coroutine-function "<< ret << std::endl;
        source();                // context-switch to coroutine-function
        std::cout<< "back from coroutine-function "<< std::endl;
    }
}

/*
例子3：主环境传递数据给协程环境
*/
void fooo(boost::coroutines2::coroutine<std::string>::pull_type & sink)
{
    std::cout << "get " << sink.get() << "from main() \n";
    sink();
    std::cout << "get " << sink.get() << "from main()\n";
    sink();
}

void test_case20_coroutine4() {

    std::string str1("hello");
    std::string str2("world");
    boost::coroutines2::coroutine<std::string>::push_type source(fooo);
    std::cout << "pass " << str1 << "to fooo()\n";
    source(str1);
    std::cout << "pass " << str2 << " to fooo()\n";
    source(str2);
}

/*
例子4：协程迭代器使用
*/
// 方法一
void foo1(boost::coroutines2::coroutine<int>::pull_type & sink){
    using coIter = boost::coroutines2::coroutine<int>::pull_type::iterator;
    for (coIter start = begin(sink); start != end(sink); ++start) {
        std::cout << "retrieve "<<*start << "\n";
    }
}

// 方法二
void foo2(boost::coroutines2::coroutine<int>::pull_type & sink) {
    for (auto val : sink) {
        std::cout << "retrieve " << val << "\n";
    }
}

// 方法三
void foo3(boost::coroutines2::coroutine<int>::pull_type & sink) {
    for (int i = 0; i < 10; i++) {
        std::cout << "retrieve " << sink.get() << "\n";
        sink();
    }
}

void test_case20_coroutine5() {
    boost::coroutines2::coroutine<int>::push_type source(foo1);
    for (int i = 0; i < 10; i++) {
        source(i);
    }
}


/*

当协程对象被创建之后就直接运行，直到sink(1)的时候暂停返回到main中，main中使用source.get()获取数据，
继续使用source()调用协程对象，协程从sink(1)之后继续运行执行完毕，返回main，main也执行完毕。

*/

void foo_case_20(boost::coroutines2::coroutine<int>::push_type & sink)
{
    std::cout<<"start coroutine\n";
    sink(1);
    std::cout<<"finish coroutine\n";
}

void test_case20_coroutine6_pull() {

    boost::coroutines2::coroutine<int>::pull_type source(foo_case_20);
    std::cout<<source.get()<<std::endl;
    source();
    std::cout<<"finish\n";

}

/*
也可以看到一个细节, 当source为pull_type的时候，协程是马上运行的，
因为此时不用传递数据进行，而push_type的时候，需要source()才会运行，第一次需要放一个没用的数据
*/
void foo_case21(boost::coroutines2::coroutine<int>::pull_type& sink)
{
    std::cout<<"start coroutine\n";
    //sink();
    int a = sink().get();
    std::cout<<a<<std::endl;
    std::cout<<"finish coroutine\n";
}

void test_case21_coroutine7_push() {
    
    boost::coroutines2::coroutine<int>::push_type source(foo_case21);

    std::cout<<"finish\n";
    source(0);
    source(5);
}


void test_case32_1() {
  
  io_service ioservice;
  steady_timer timer(ioservice, std::chrono::seconds(3));
  timer.async_wait([](const boost::system::error_code &ec)
  { std::cout << "3 sec\n"; });
  ioservice.run();
}

void test_case32_2() {
  
  io_service ioservice;
  steady_timer timer1{ioservice, std::chrono::seconds{3}};
  timer1.async_wait([](const boost::system::error_code &ec)
    { std::cout << "3 sec\n"; });
 
  steady_timer timer2{ioservice, std::chrono::seconds{4}};
  timer2.async_wait([](const boost::system::error_code &ec)
    { std::cout << "4 sec\n"; });
  ioservice.run();

}

/*
示例32.3中，两个定时器都在3秒后报警，由于是两个线程，两个lambda函数能够同时运行。
如果第二个报警产生时，第一个报警句柄函数正在执行，那么第二个句柄函数能在第二个线程执行。
如果第一个句柄函数已经返回，那么I/O服务对象可用任何一个线程执行第二个句柄函数。
*/
void test_case32_3() {
    
  io_service ioservice;
 
  steady_timer timer1{ioservice, std::chrono::seconds{3}};
  timer1.async_wait([](const boost::system::error_code &ec)
    { std::cout << "3 sec\n"; });
 
  steady_timer timer2{ioservice, std::chrono::seconds{3}};
  timer2.async_wait([](const boost::system::error_code &ec)
    { std::cout << "3 sec\n"; });
 
  std::thread thread1{[&ioservice](){ ioservice.run(); }};
  std::thread thread2{[&ioservice](){ ioservice.run(); }};
  thread1.join();
  thread2.join();

}

/*
基于Boost.Asio的应用的推荐方法是在一个I/O服务对象上多次调用run()，这样扩展性更好，
但是，你也可以不采用多个线程一个I/O服务对象的方式，而采用多个I/O服务对象方式。

示例32.4中，用了boost::asio::steady_timer类型的两个定时器的同时用了两个I/O服务对象，
这个程序基于两个线程，一个线程对应一个I/O服务对象，两个I/O对象timer1和timer2分别对应两个I/O服务对象，而不是一个。

示例32.4与上面的示例工作相同。对于什么情况下使用多个I/O服务对象，不太可能给出一般性的建议。
因为boost::asio::io_service封装了操作系统接口，任何行为都是由特定接口决定的。

Windows平台，boost::asio::io_service通常基于IOCP，Linux平台，它通常基于epoll()。
有几个I/O服务对象就意味着有几个I/O完成端口或epoll()被调用多次，这种用法是否会比用一个I/O完成端口或调用一次epoll()好，取决于特定的场合。

*/
void test_case32_4() {

  io_service ioservice1;
  io_service ioservice2;
 
  steady_timer timer1{ioservice1, std::chrono::seconds{3}};
  timer1.async_wait([](const boost::system::error_code &ec)
    { std::cout << "3 sec\n"; });
 
  steady_timer timer2{ioservice2, std::chrono::seconds{3}};
  timer2.async_wait([](const boost::system::error_code &ec)
    { std::cout << "3 sec\n"; });
 
  std::thread thread1{[&ioservice1](){ ioservice1.run(); }};
  std::thread thread2{[&ioservice2](){ ioservice2.run(); }};
  thread1.join();
  thread2.join();
}


/*

对async_connect()的调用会导致connect_handler()被调用，同样，首先检查ec，看看是否连接已建立，如果如此，
在这个socket上调用async_read_some()，接收数据操作开始，接收的数据保存在bytes数组中，它是传递给async_read_some()的第一个参数。

当接收到一个或多个字节的数据并拷贝到bytes后，read_handler()被调用，std::size_t类型的
参数bytes_transferred含有接收到的字节数。一般情况下，这个句柄函数首先应检查ec，看看异步操作是否成功，仅在成功情况下，接收的数据输出到标准输出流上。

请注意，read_handler()在数据被输出到std::cout之后又一次调用async_read_some()，这样做是有必要的，因为你不能确定要下载
的整个主页的内容能在一次异步操作中完成。不断地调用async_read_some()以及read_handler()，直到连接被关闭为止，
read_handler()中的ec是一个错误值。此时web server已发送完整个主页，不再有数据输出到std::cout，async_read()也不再被调用。由于没有被挂起的异步操作，程序退出。

*/

    using namespace boost::asio;
    using namespace boost::asio::ip;
    
    io_service ioservice;
    tcp::resolver resolv{ioservice};
    tcp::socket tcp_socket{ioservice};
    std::array<char, 4096> bytes;
    
    void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
    {
    if (!ec)
    {
        std::cout.write(bytes.data(), bytes_transferred);
        tcp_socket.async_read_some(buffer(bytes), read_handler);
    }
    }
    
    void connect_handler(const boost::system::error_code &ec)
    {
    if (!ec)
    {
        std::string r =
        "GET / HTTP/1.1\r\nHost: theboostcpplibraries.com\r\n\r\n";
        write(tcp_socket, buffer(r));
        tcp_socket.async_read_some(buffer(bytes), read_handler);
    }
    }
    
    void resolve_handler(const boost::system::error_code &ec, tcp::resolver::iterator it)
    {
    if (!ec)
        tcp_socket.async_connect(*it, connect_handler);
    }

    void test_case32_5() {
        tcp::resolver::query q{"theboostcpplibraries.com", "80"};
        resolv.async_resolve(q, resolve_handler);
        ioservice.run();
    }


int main() {

    test_case0_p11_boost_version();
    //test_case1();
    //test_case2();dsfa
    //test_case3_p551_async_timer();
    //test_case4_idp_address();
    //test_case5_syfsanc_tcp_server();
    //test_case4_p557_endpoint();
    //test_case5__p561_sync_tcp_server();
    //test_case6_p561_sync_tcp_client();
    //test_case6_p561_sync_tcp_client_small_packet();
    //test_case7_p564_async_tcp_server();
    //test_case8_p565_async_tcp_client();
    //test_case9_p572_async_coroutine_tcp_server();
    //test_case9_p572_async_coroutine_tcp_server();
    //test_case10_coroutines2();
    //test_case11_p573_async_connect_timeout();
    //test_case12_p575_udp_server();
    //test_case12_p575_udp_client();
    //test_case20_coroutine2();
    //test_case20_coroutine3();
    //test_case20_coroutine4();
    //test_case20_coroutine5();
    //test_case20_coroutine6();
    //test_case21_coroutine7_push();
    //test_case32_1();
    //test_case32_2();
    //test_case32_3();
    //test_case32_4();
    test_case32_5();
    return 0;
}   